package com.lmy.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication(scanBasePackages = {"com.lmy", "com"})
public class MainApp {

    public static void main(String[] args) {

        //当执行run方法时，是怎样启动的内置tomcat
        ConfigurableApplicationContext ioc
                = SpringApplication.run(MainApp.class, args);

        /*
            通过debug源码分析SpringApplication.run是如何启动tomcat的
            1.SpringApplication.java
              public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
                return run(new Class[]{primarySource}, args);
              }

            2.SpringApplication.java；创建返回 ConfigurableApplicationContext 接口类型
               public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
                return (new SpringApplication(primarySources)).run(args);
              }

            3.SpringApplication.java
               public ConfigurableApplicationContext run(String... args) {
                StopWatch stopWatch = new StopWatch();
                stopWatch.start();
                DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
                ConfigurableApplicationContext context = null;
                this.configureHeadlessProperty();
                SpringApplicationRunListeners listeners = this.getRunListeners(args);
                listeners.starting(bootstrapContext, this.mainApplicationClass);

                try {
                    ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
                    ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
                    this.configureIgnoreBeanInfo(environment);
                    Banner printedBanner = this.printBanner(environment);
                    context = this.createApplicationContext();  //分析此方法，创建spring容器
                    context.setApplicationStartup(this.applicationStartup);
                    this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
                    this.refreshContext(context);   //分析此方法，刷新应用程序上下文(准备应用程序的运行环境)，比如初始化默认设置/注入相关bean、启动tomcat
                    this.afterRefresh(context, applicationArguments);
                    stopWatch.stop();
                    if (this.logStartupInfo) {
                        (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
                    }

                    listeners.started(context);
                    this.callRunners(context, applicationArguments);
                } catch (Throwable ex) {
                    this.handleRunFailure(context, ex, listeners);
                    throw new IllegalStateException(ex);
                }

                try {
                    listeners.running(context);
                    return context;
                } catch (Throwable ex) {
                    this.handleRunFailure(context, ex, (SpringApplicationRunListeners)null);
                    throw new IllegalStateException(ex);
                }
              }
             4.SpringApplication.java；容器类型很多，会根据 webApplicationType 创建对应的容器
               默认是SERVLET,也就是web容器
                protected ConfigurableApplicationContext createApplicationContext() {
                    return this.applicationContextFactory.create(this.webApplicationType);
                }
             5.ApplicationContextFactory.java
                 ApplicationContextFactory DEFAULT = (webApplicationType) -> {
                    try {
                        for(ApplicationContextFactory candidate : SpringFactoriesLoader.loadFactories(ApplicationContextFactory.class, ApplicationContextFactory.class.getClassLoader())) {
                            ConfigurableApplicationContext context = candidate.create(webApplicationType);
                            if (context != null) {
                                return context;
                            }
                        }

                        return new AnnotationConfigApplicationContext();    //返回web容器
                    } catch (Exception ex) {
                        throw new IllegalStateException("Unable create a default ApplicationContext instance, you may need a custom ApplicationContextFactory", ex);
                    }
                };
             6.SpringApplication.java
                private void refreshContext(ConfigurableApplicationContext context) {
                    if (this.registerShutdownHook) {
                        shutdownHook.registerApplicationContext(context);
                    }

                    this.refresh(context);  // 分析此方法
                }
              7.SpringApplication.java
                protected void refresh(ConfigurableApplicationContext applicationContext) {
                    applicationContext.refresh();
                }
              8.ServletWebServerApplicationContext.java
                public final void refresh() throws BeansException, IllegalStateException {
                    try {
                        super.refresh();    //分析此方法
                    } catch (RuntimeException ex) {
                        WebServer webServer = this.webServer;
                        if (webServer != null) {
                            webServer.stop();
                        }

                        throw ex;
                    }
                }
               9.AbstractApplicationContext.java
                 public void refresh() throws BeansException, IllegalStateException {
                    synchronized(this.startupShutdownMonitor) {
                        StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
                        this.prepareRefresh();
                        ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
                        this.prepareBeanFactory(beanFactory);

                        try {
                            this.postProcessBeanFactory(beanFactory);
                            StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
                            this.invokeBeanFactoryPostProcessors(beanFactory);
                            this.registerBeanPostProcessors(beanFactory);
                            beanPostProcess.end();
                            this.initMessageSource();
                            this.initApplicationEventMulticaster();
                            this.onRefresh();   //分析此方法，当父容器完成了通用初始化工作后，再利用动态绑定机制回到子类完成刷新工作
                            this.registerListeners();
                            this.finishBeanFactoryInitialization(beanFactory);
                            this.finishRefresh();
                        } catch (BeansException var10) {
                            if (this.logger.isWarnEnabled()) {
                                this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var10);
                            }

                            this.destroyBeans();
                            this.cancelRefresh(var10);
                            throw var10;
                        } finally {
                            this.resetCommonCaches();
                            contextRefresh.end();
                        }

                        }
                    }
                10.ServletWebServerApplicationContext.java
                    protected void onRefresh() {
                        super.onRefresh();

                        try {
                            this.createWebServer(); //分析此方法，创建webServer，即web服务-Tomcat
                        } catch (Throwable ex) {
                            throw new ApplicationContextException("Unable to start web server", ex);
                        }
                    }
                11.ServletWebServerApplicationContext.java
                    private void createWebServer() {
                        WebServer webServer = this.webServer;
                        ServletContext servletContext = this.getServletContext();
                        if (webServer == null && servletContext == null) {
                            StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
                            ServletWebServerFactory factory = this.getWebServerFactory();
                            createWebServer.tag("factory", factory.getClass().toString());
                            //创建一个tomcatWebServer
                            this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()});
                            createWebServer.end();
                            this.getBeanFactory().registerSingleton("webServerGracefulShutdown", new WebServerGracefulShutdownLifecycle(this.webServer));
                            this.getBeanFactory().registerSingleton("webServerStartStop", new WebServerStartStopLifecycle(this, this.webServer));
                        } else if (servletContext != null) {
                            try {
                                this.getSelfInitializer().onStartup(servletContext);
                            } catch (ServletException ex) {
                                throw new ApplicationContextException("Cannot initialize servlet context", ex);
                            }
                        }

                        this.initPropertySources();
                    }
                  12.TomcatServletWebServerFactory.java：会创建一个tomcat并启动tomcat
                      public WebServer getWebServer(ServletContextInitializer... initializers) {
                        if (this.disableMBeanRegistry) {
                            Registry.disableRegistry();
                        }

                        Tomcat tomcat = new Tomcat();   //创建了 Tomcat 对象
                        File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat");
                        tomcat.setBaseDir(baseDir.getAbsolutePath());

                        for(LifecycleListener listener : this.serverLifecycleListeners) {
                            tomcat.getServer().addLifecycleListener(listener);
                        }

                        Connector connector = new Connector(this.protocol);
                        connector.setThrowOnFailure(true);
                        tomcat.getService().addConnector(connector);
                        this.customizeConnector(connector);
                        tomcat.setConnector(connector);
                        tomcat.getHost().setAutoDeploy(false);
                        this.configureEngine(tomcat.getEngine());

                        for(Connector additionalConnector : this.additionalTomcatConnectors) {
                            tomcat.getService().addConnector(additionalConnector);
                        }

                        this.prepareContext(tomcat.getHost(), initializers);
                        return this.getTomcatWebServer(tomcat); //分析此方法
                       }
                  13.TomcatServletWebServerFactory.java：这里做了校验，创建了TomcatWebServer
                    protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
                        return new TomcatWebServer(tomcat, this.getPort() >= 0, this.getShutdown());
                    }
                  14.TomcatWebServer.java
                    public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) {
                        this.monitor = new Object();
                        this.serviceConnectors = new HashMap();
                        Assert.notNull(tomcat, "Tomcat Server must not be null");
                        this.tomcat = tomcat;
                        this.autoStart = autoStart;
                        this.gracefulShutdown = shutdown == Shutdown.GRACEFUL ? new GracefulShutdown(tomcat) : null;
                        this.initialize();  //分析此方法
                    }
                  15.TomcatWebServer.java
                      private void initialize() throws WebServerException {
                        logger.info("Tomcat initialized with port(s): " + this.getPortsDescription(false));
                        synchronized(this.monitor) {
                            try {
                                this.addInstanceIdToEngineName();
                                Context context = this.findContext();
                                context.addLifecycleListener((event) -> {
                                    if (context.equals(event.getSource()) && "start".equals(event.getType())) {
                                        this.removeServiceConnectors();
                                    }

                                });
                                this.tomcat.start();    //启动tomcat服务器
                                this.rethrowDeferredStartupExceptions();

                                try {
                                    ContextBindings.bindClassLoader(context, context.getNamingToken(), this.getClass().getClassLoader());
                                } catch (NamingException var5) {
                                }

                                this.startDaemonAwaitThread();
                            } catch (Exception ex) {
                                this.stopSilently();
                                this.destroySilently();
                                throw new WebServerException("Unable to start embedded Tomcat", ex);
                            }

                        }
                      }
         */
        System.out.println("hello ioc");
    }
}
